package jpavlich.cardsorter.sound;

/*
 *	AudioRecorder.java
 *
 *	This file is part of jsresources.org
 */

/*
 * Copyright (c) 1999 - 2003 by Matthias Pfisterer
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 |<---            this code is formatted to fit into 80 columns             --->|
 */

import java.io.File;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.TargetDataLine;

public class AudioRecorder {
	private static final SupportedFormat[] SUPPORTED_FORMATS = {
			new SupportedFormat("s8", AudioFormat.Encoding.PCM_SIGNED, 8, true),
			new SupportedFormat("u8", AudioFormat.Encoding.PCM_UNSIGNED, 8, true),
			new SupportedFormat("s16_le", AudioFormat.Encoding.PCM_SIGNED, 16, false),
			new SupportedFormat("s16_be", AudioFormat.Encoding.PCM_SIGNED, 16, true),
			new SupportedFormat("u16_le", AudioFormat.Encoding.PCM_UNSIGNED, 16, false),
			new SupportedFormat("u16_be", AudioFormat.Encoding.PCM_UNSIGNED, 16, true),
			new SupportedFormat("s24_le", AudioFormat.Encoding.PCM_SIGNED, 24, false),
			new SupportedFormat("s24_be", AudioFormat.Encoding.PCM_SIGNED, 24, true),
			new SupportedFormat("u24_le", AudioFormat.Encoding.PCM_UNSIGNED, 24, false),
			new SupportedFormat("u24_be", AudioFormat.Encoding.PCM_UNSIGNED, 24, true),
			new SupportedFormat("s32_le", AudioFormat.Encoding.PCM_SIGNED, 32, false),
			new SupportedFormat("s32_be", AudioFormat.Encoding.PCM_SIGNED, 32, true),
			new SupportedFormat("u32_le", AudioFormat.Encoding.PCM_UNSIGNED, 32, false),
			new SupportedFormat("u32_be", AudioFormat.Encoding.PCM_UNSIGNED, 32, true), };

	private static final String DEFAULT_FORMAT = "s16_le";
	private static final int DEFAULT_CHANNELS = 2;
	private static final float DEFAULT_RATE = 44100.0F;
	private static final AudioFileFormat.Type DEFAULT_TARGET_TYPE = AudioFileFormat.Type.WAVE;

	static boolean sm_bDebug = false;

	private Recorder recorder;

	private String mixerName;

	private int internalBufferSize;

	private int channels;

	private float rate;

	private String extension;

	private boolean directRecording;

	private AudioFormat audioFormat;

	public AudioRecorder(String filename) {
		mixerName = null;
		internalBufferSize = AudioSystem.NOT_SPECIFIED;
		String format = DEFAULT_FORMAT;
		channels = DEFAULT_CHANNELS;
		rate = DEFAULT_RATE;
		extension = null;
		directRecording = true;

		setFormat(format);

		AudioFileFormat.Type targetType = null;
		File outputFile = new File(filename);

		if (extension == null) {
			/*
			 * The user chose not to specify a target audio file type
			 * explicitely. We are trying to guess the type from the target file
			 * name extension.
			 */
			int nDotPosition = filename.lastIndexOf('.');
			if (nDotPosition != -1) {
				extension = filename.substring(nDotPosition + 1);
			}
		}
		if (extension != null) {
			targetType = AudioCommon.findTargetType(extension);
			if (targetType == null) {
				System.out.println("target type '" + extension + "' is not supported.");
				System.out.println("using default type '" + DEFAULT_TARGET_TYPE.getExtension() + "'");
				targetType = DEFAULT_TARGET_TYPE;
			}
		} else {
			System.out
					.println("target type is neither specified nor can be guessed from the target file name.");
			System.out.println("using default type '" + DEFAULT_TARGET_TYPE.getExtension() + "'");
			targetType = DEFAULT_TARGET_TYPE;
		}
		if (sm_bDebug) {
			System.out.println("AudioRecorder.main(): target audio file format type: " + targetType);
		}

		TargetDataLine targetDataLine = null;
		targetDataLine = AudioCommon.getTargetDataLine(mixerName, audioFormat, internalBufferSize);
		if (targetDataLine == null) {
			System.out.println("can't get TargetDataLine, exiting.");
			System.exit(1);
		}

		if (directRecording) {
			recorder = new DirectRecorder(targetDataLine, targetType, outputFile);
		} else {
			recorder = new BufferingRecorder(targetDataLine, targetType, outputFile);
		}
		if (sm_bDebug) {
			System.out.println("AudioRecorder.main(): Recorder: " + recorder);
		}

	}

	private void setFormat(String format) {
		/*
		 * For convenience, we have some shortcuts to set the properties needed
		 * for constructing an AudioFormat.
		 */
		if (format.equals("phone")) {
			// 8 kHz, 8 bit unsigned, mono

			rate = 8000.0F;
			format = "u8";
			channels = 1;
		} else if (format.equals("radio")) {
			// 22.05 kHz, 16 bit signed, mono
			rate = 22050.0F;
			format = "s16_le";
			channels = 1;
		} else if (format.equals("cd")) {
			// 44.1 kHz, 16 bit signed, stereo, little-endian
			rate = 44100.0F;
			format = "s16_le";
			channels = 2;
		} else if (format.equals("dat")) {
			// 48 kHz, 16 bit signed, stereo, little-endian
			rate = 48000.0F;
			format = "s16_le";
			channels = 2;
		}

		/*
		 * Here, we are constructing the AudioFormat to use for the recording.
		 * Sample rate (fRate) and number of channels (nChannels) are already
		 * set safely, since they have default values set at the very top. The
		 * other properties needed for AudioFormat are derived from the 'format'
		 * specification (strFormat).
		 */
		int nOutputFormatIndex = -1;
		for (int i = 0; i < SUPPORTED_FORMATS.length; i++) {
			if (SUPPORTED_FORMATS[i].getName().equals(format)) {
				nOutputFormatIndex = i;
				break;
			}
		}
		/*
		 * If we haven't found the format (string) requested by the user, we
		 * switch to a default format.
		 */
		if (nOutputFormatIndex == -1) {
			System.out.println("warning: output format '" + format
					+ "' not supported; using default output format '" + DEFAULT_FORMAT + "'");
			/*
			 * This is the index of "s16_le". Yes, it's a bit quick & dirty to
			 * hardcode the index here.
			 */
			nOutputFormatIndex = 2;
		}
		AudioFormat.Encoding encoding = SUPPORTED_FORMATS[nOutputFormatIndex].getEncoding();
		int nBitsPerSample = SUPPORTED_FORMATS[nOutputFormatIndex].getSampleSize();
		boolean bBigEndian = SUPPORTED_FORMATS[nOutputFormatIndex].getBigEndian();
		int nFrameSize = (nBitsPerSample / 8) * channels;
		audioFormat = new AudioFormat(encoding, rate, nBitsPerSample, channels, nFrameSize, rate, bBigEndian);
		if (sm_bDebug) {
			System.out.println("AudioRecorder.main(): target audio format: " + audioFormat);
		}
	}

	public void stop() {
		recorder.stopRecording();
	}

	public void record() {
		recorder.start();
	}

	public static void main(String args[]) {
		AudioRecorder audioRecorder = new AudioRecorder("a.wav");
		System.out.println("recording");
		audioRecorder.record();
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
		}
		audioRecorder.stop();
		System.out.println("stop");
		audioRecorder = new AudioRecorder("b.wav");
		System.out.println("recording");
		audioRecorder.record();
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
		}
		audioRecorder.stop();

	}

}